#! /usr/bin/env python

#############################################################################
# torrentdb.py: Runs the torrent database.
#############################################################################
# Copyright (C) 2012
#
# Authors: Dan Larkin <danielhlarkin@gmail.com>
#          Yonatan Naamad <cached@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#############################################################################

import os
import bencode
import hashlib
import socket
import thread
import sys
import math
import time
import struct
import sqlite3

OP_LIST = 4
OP_EXPOSIT = 5
OP_GET = 6
OP_GIVE = 7

def compute_hash(torrent_data):
    metainfo = bencode.bdecode(torrent_data)
    return hashlib.sha1(bencode.bencode(metainfo['info'])).digest()

class TorrentDB(object):
    def __init__(self, db_file, port):
        self.db_file = db_file
        self.port = port
        
        self.db_file = db_file
        
        self.listen()
            
    def listen(self,port = None):
        '''Listen and handle requests.'''
        if port == None:
            port = self.port
            
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(5.000)
        
        s.bind(('', port))
        s.listen(20)
        print "Server started..."
        
        while 1:
            conn, addr = None, None
            while not conn and not addr:
                try:
                    conn, addr = s.accept()
                except:
                    continue
                    
            try:
                thread.start_new_thread(self.handle_req, (conn, addr))
            except:
                print e
                pass
            
    def handle_req(self, conn, addr):
        '''Handle the request from the user, and then close the connection.'''
        op = ord(conn.recv(1))
                
        if op == OP_LIST:
            #List request. Send out a 32-bit int for the number of keys
            #about to be sent, followed by the list of keys.
            
            #connect to database
            db_conn = sqlite3.connect(self.db_file, isolation_level = None)
            c = db_conn.cursor()

            
            #get user key
            #key = sqlite3.Binary(conn.recv(16))
            key = conn.recv(16)

            c.execute("""SELECT hash, size, t_min, t_max FROM torrents INNER JOIN users on
                         permissions.user_id=users.id AND users.key = ?
                        INNER JOIN permissions ON permissions.torrent_id = torrents.id;"""
                      , (key,))
                      
            print """SELECT hash, size, t_min, t_max FROM torrents INNER JOIN users on
                         permissions.user_id=users.id AND users.key = '%s'
                        INNER JOIN permissions ON permissions.torrent_id = torrents.id;""" % (key,)  
            
            res = c.fetchall()
            print res

            data = struct.pack("!BI" + (" 20s I I I" * len(res)), OP_EXPOSIT, len(res),
                               *sum(map(lambda x: [str(x[0])]+list(x[1:4]), res),[]))
                               
            conn.send(data)
            c.close()

        elif op == OP_GET:
            #Specific hash request. Send out a 32-bit int for the length of the
            #the torrent file (if it's in the dict) followed by the file itself.
            #Send out 4 zero bytes otherwise (i.e. on a failed lookup).
            
            #connect to database
            db_conn = sqlite3.connect(db_file, isolation_level = None)
            c = db_conn.cursor()
            
            t_hash = sqlite3.Binary(conn.recv(20))
            
            c.execute("SELECT path FROM torrents WHERE hash=? LIMIT 1;", (t_hash,))
            try:
                path = c.fetchone()
                f = open(path[0],'rb')
                s = f.read()
                f.close()
                data = struct.pack("!Bi%ds" % len(s), OP_GIVE, len(s), s)
                conn.send(data)
            except:
                conn.send(struct.pack("!Bi", OP_GIVE, 0))
                
            c.close()
                
        conn.close()
        
if __name__ == '__main__':
    if len(sys.argv) != 3:
        print "Usage: ./%s <db_file> <port_number>" % sys.argv[0]
    else:
        db_file = sys.argv[1]
        port = sys.argv[2]
        t = TorrentDB(db_file, int(port))
